﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace Common
{
    public class Document:IEnumerable<Field>
    {
        private long Id;

     
        public enum Type { Story, Talkback, Blog, ForumMessage }
        public enum Section { Title, Summary, Text, Author }
        public Field[] Fields;
        public int NumberOfFields;
        public DateTime DateTime;
        public Uri Url;
        public long CommentNumber; //If this document is a forum message/talkback, this is the ID given by the site.
        private int TalkbackNumber; //If this document is a talkback, what number is it in the article.
        private int SentimentID;
        private DateTime downloadDateTime;
   
        private Type type;

        public string SiteName;
        private string DisplayName;

        //For ranking : 
        public int SitePriority = 0; //User Defined. 0 is default if not defined.
        public double Score = 0; //Document's BM25F + Proximity score.

        //For storing query match excerpts.
        public string[] Excerpts;

        public Document(long id, DateTime datetime, Uri url, long commentNumber,int talkbackNumber, Type Type, int priority, string siteName)
        {
            Id = id;
            DateTime = datetime;
            //** document is identified uniquely by Url + CommentNumber
            Url = url; 
            CommentNumber = commentNumber;
            //**
            type = Type;
            SitePriority = priority;
            //Add fields according to FieldType Enum.
            Section ft = new Section();
            NumberOfFields = Enum.GetNames(ft.GetType()).Length;
            Fields = new Field[NumberOfFields];
            TalkbackNumber = talkbackNumber;
            SiteName = siteName;
        }
		public override string ToString()
		{
			return Fields[(int)Document.Section.Title].Text;
		}
        public override int GetHashCode()
        {
            return Url.GetHashCode() ^ CommentNumber.GetHashCode();
        }

        public override bool Equals(Object obj)
        {
            //Check for null and compare run-time types.
            if (obj == null || GetType() != obj.GetType()) return false;
            Document doc = obj as Document;
            return ( doc.Url == Url && doc.CommentNumber == CommentNumber);
        }

		public static List<Document> SortDocumentsByPriorityDate(List<Document> docs)
		{
			var sorted = from d in docs orderby

						 d.SitePriority descending,
						 d.DateTime descending,
						 d.Score descending

						 select d;

			return new List<Document>(sorted);
		}

        public static List<Document> FilterDocumentsByDate(List<Document> docs, DateTime FromDate, DateTime ToDate)
        {
            var filtered = from d in docs
                         where
                             d.DateTime >= FromDate &&
                             d.DateTime <= ToDate

                         select d;
            return new List<Document>(filtered);
        }
        public static List<Document> FilterDocumentsByDownloadDateTime(List<Document> docs, DateTime FromDate, DateTime ToDate)
        {
            var filtered = from d in docs
                           where
                               d.DownloadDateTime >= FromDate &&
                               d.DownloadDateTime <= ToDate

                           select d;
            return new List<Document>(filtered);
        }

        public static DateTime GetDocumentMinDate(List<Document> docs)
        {
            var mindate = from d in docs
                          where d.DateTime == docs.Min(doc2 => doc2.DateTime)
                          select d;
            
            return (mindate.ToArray())[0].DateTime;
                
        }
        public static DateTime GetDocumentMaxDate(List<Document> docs)
        {
            var mindate = from d in docs
                          where d.DateTime == docs.Max(doc2 => doc2.DateTime)
                          select d;
            return (mindate.ToArray())[0].DateTime;

        }

        public string[] GetHTMLExcerpts(string[] queries, Document.Section section,
            int numOfExcerpts, int numBefore, int numAfter, HebrewStemmer hs)
        {
            Hashtable QueryExact = new Hashtable();
            List<string> excerpts = new List<string>();
            string[] fieldWords = this.Fields[(int)section].Text.Split(new char[] {' ', ',', '!', '?', '\"', '(', ')'});
            //Split all queries into single words and make one long array of them.

            for (int i = 0; i < queries.Length; ++i)
            {
                var res = QueryParser.GetParsedPhraseArray(queries[i], false);
                foreach (string word in res) //for each query word
                {
                    //If Exact match word.
                    if (word.StartsWith(QueryParser.ExactMatchString) && word.EndsWith(QueryParser.ExactMatchString))
                    {
                        
                        string w = word;
                        w = word.Trim(new char[] { QueryParser.ExactMatchCharacter });
                        if (QueryExact.Contains(w))
                            continue;
                        QueryExact.Add(w, true);
                    }
                    else
                    {
                        if (QueryExact.Contains(word))
                        {
                            if (((bool)QueryExact[word]) == true)
                                QueryExact[word] = false;
                        }
                        else
                            QueryExact.Add(word, false);
                    }
                } 
            }
     
            int[] positions = new int[fieldWords.Length];
            int[] positionsNoChange = new int[fieldWords.Length];

            if (null == hs) 
                return excerpts.ToArray();
            foreach (DictionaryEntry QueryIsExact in QueryExact)
            {

                int numOfAppearances = 0;
                string word = QueryIsExact.Key as string;
                bool IsExact = (bool)QueryIsExact.Value;
                string[] words;
                if (!IsExact)
                {
                    words = (hs.GetStemmedWords(word).ToArray(typeof(string)) as string[]);
                    words = words.Concat(new string[] { word }).ToArray();
                }
                else
                {
                    words = new string[1];
                    words[0] = word;
                }
                for (int i = 0; i < fieldWords.Length; ++i) //for each word in field
                {
                   
                    string matchWord = fieldWords[i];
                    matchWord = QueryParser.ParseWord(matchWord, false);
                    string[] matchWords = (hs.GetStemmedWords(matchWord).ToArray(typeof(string)) as string[]);
                    matchWords = matchWords.Concat(new string[] { matchWord }).ToArray();
                    if (Intersection(matchWords, words).Length > 0)
                    {
                        positionsNoChange[i] = 1; //positions of all words
                        if (numOfAppearances < numOfExcerpts)
                            positions[i] = 1; //position of excerpt words
                        ++numOfAppearances; 
                    }
                  
                   
                }

            }
           
           //Now we have position arrays , we start finding excerpts.
            for (int k = 0; k < positions.Length; ++k)
            {
                if (positions[k] == 1)
                {
                    StringBuilder temp = new StringBuilder();
                    temp.Append("...");

                    int i = k;
                    int top = ((i + numAfter) > (fieldWords.Length - 1)) ? fieldWords.Length - 1 : i + numAfter;
                    for (int j = (i - numBefore < 0) ? 0 : i - numBefore;
                        j < top; ++j)
                    {
                        if (positionsNoChange[j] == 1) //This is an excerpt for a specific search word.
                        {
                            temp.Append(String.Format(" <B>{0}</B>", fieldWords[j]));
                            positions[j] = 0;  //This word needs no more excerpts.
                        }
                        else //normal word
                        {
                            temp.Append(String.Format(" {0}", fieldWords[j]));
                        }
                    }
                    temp.Append("...");

                    excerpts.Add(temp.ToString());
                }

            }


            return excerpts.ToArray();
        }

        public static string[] Intersection(string[] arrayA, string[] arrayB)
        {
            ArrayList outArray = new ArrayList();
            for (int i = 0; i < arrayA.Length; i++)
            {
                for (int j = 0; j < arrayB.Length; j++)
                {
                    if (arrayA[i] == arrayB[j] && outArray.IndexOf(arrayB[j]) < 0)
                        outArray.Add(arrayB[j]);
                }
            }
            return (String[])outArray.ToArray(typeof(string));
        }


        public static string DocumentTypeToString(Type type)
        {
            string TypeString = string.Empty;
            switch (type)
            {
                case Type.Talkback:
                    TypeString = "תגובה";
                    break;
                case Type.Story:
                    TypeString = "כתבה";
                    break;
                case Type.ForumMessage:
                    TypeString = "פורום";
                    break;
                case Type.Blog:
                    TypeString = "בלוג";
                    break;
            }
            return TypeString;
        }

        public string DocumentTypeToString()
        {
            string TypeString = string.Empty;
            return DocumentTypeToString(this.type);
        }

        public static List<string> GetDocumentTypesNameList()
        {
            List<string> res = new List<string>();
            foreach (Type type in Enum.GetValues(typeof(Type)))
            {
                res.Add(DocumentTypeToString(type));
            }
            return res;
        }

        public string GetDocumentDisplayName(SiteNames siteNames)
        {
            string DisplayName = string.Empty;
            switch (this.type)
            {
                case Type.Talkback:
                    DisplayName = siteNames.TalkbackName;
                    break;
                case Type.Story:
                    DisplayName = siteNames.StoryName;
                    break;
                case Type.ForumMessage:
                    DisplayName = siteNames.ForumName;
                    break;
                case Type.Blog:
                    DisplayName = siteNames.BlogName;
                    break;
            }
            return DisplayName;
        }

        #region IEnumerable<Field> Members

        public IEnumerator<Field> GetEnumerator()
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return Fields.GetEnumerator();
        }

        #endregion

        #region FieldProperties

        public string Title
        {
            get { return Fields[(int)Document.Section.Title].Text; }
        }
        public string Summary
        {
            get { return Fields[(int)Document.Section.Summary].Text; }
        }
        public string Text
        {
            get { return Fields[(int)Document.Section.Text].Text; }
        }
        public string Author
        {
            get { return Fields[(int)Document.Section.Author].Text; }
        }
        public DateTime dateTime
        {
            get { return DateTime; }
        }
        public Uri url
        {
            get { return Url; }
        }
        public string[] excerpts
        {
            get { return Excerpts; } 
        }

        public Type DocType
        {
            get { return type; }
            set { type = value; }
        }

        public int SentimentId
        {
            get { return SentimentID; }
            set { SentimentID = value; }
        }

        public int talkbackNumber
        {
            get { return TalkbackNumber; }
            set { TalkbackNumber = value; }
        }
        public long ID
        {
            get { return Id; }
            set { Id = value; }
        }

        public DateTime DownloadDateTime
        {
            get { return downloadDateTime; }
            set { downloadDateTime = value; }
        }

        public string DisplayNameProperty
        {
            get { return DisplayName; }
            set { DisplayName = value; }
        }
        #endregion
    }
}
